package com.noble.meether.chat.unread;

import android.text.TextUtils;
import android.view.View;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.noble.library.log.PLogger;
import com.noble.library.log.PSP;
import com.noble.library.observe.MsgMgr;
import com.noble.library.observe.MsgType;
import com.noble.meether.chat.unread.view.BadgeView;

import java.util.HashMap;
import java.util.Map;

/**
 * 未读角标管理manager
 */
public class UnreadMgr {

    private volatile static UnreadMgr instance = null;

    public static UnreadMgr getInstance() {
        if (instance == null) {
            synchronized (UnreadMgr.class) {
                if (instance == null) {
                    instance = new UnreadMgr();
                }
            }
        }
        return instance;
    }

    // =================================== init ===================================
    /* ======从MT_Unread_change消息类型getData或得到的Map中获取值的key====== */
    public static final String Msg_Name_Key = "name";       //角标消息中角标名称的获取key
    public static final String Msg_Status_Key = "status";   //角标消息中角标增删状态的获取key

    /* 未读消息的级联关系，每次添加新的层级角标之后在此进行配置 */
    private Map<String, String[]> parentMap = new HashMap<>();

    /* 对应未读消息的存储对象 */
    private Map<String, Integer> unreadMap = new HashMap<>();

    /* 消息列表未读， 用户Uid作为 key, 供未读查询 */
    private Map<Long, Integer> unreadMsgListMap = new HashMap<>();

    /**
     * 初始化角标系统
     *
     * @param storeString 存储的角标信息json字符串，可由外部调用选择存储至数据库或SP等
     * @param parentMap   子级和父级的级联关系。只关心最小元素的子级，如：消息tab中有一个最近消息栏目，最近消息中又包括好友和陌生人，
     *                    这时候好友和陌生人就是最小的元素，由这两个最小元素的添加引起了最近消息和消息总数的添加。<p>
     *                    Map<String, String[]> parentMap = new HashMap<String, String[]>();<br>
     *                    parentMap.put("friends",new String[]{"message","recentMessage"});<br>
     *                    parentMap.put("strangers",new String[]{"message","recentMessage"});<p>
     *                    以上演示了一个三级标示消息的结构，二级的结构如下：<br>
     *                    parentMap.put("friends",new String[]{"message","lookedMe"});<p>
     *                    只有一级的标示消息无需添加父级层联关系的map。
     */
    public void init(String storeString, Map<String, String[]> parentMap) {
        this.parentMap = (parentMap == null ? new HashMap<>() : parentMap);
        unreadMap.clear();//应用未杀死时切换用户，清除上个用户的信息

        PLogger.d("getUnreadMessage: --------->stored string：" + storeString);
        if (TextUtils.isEmpty(storeString)) return;

        try {
            unreadMap = JSON.parseObject(storeString, new TypeReference<Map<String, Integer>>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (unreadMap == null) {
                unreadMap = new HashMap<>();
            }
        }
    }

    // ============================== 消息列表未读： 外部调用(仅供消息列表使用) ==============================
    public static long whisperId = 0;  // 正在私聊的用户，供消息列表角标处理使用

    /**
     * 消息列表未读初始化
     */
    public void initMsgListUnread() {
        unreadMsgListMap.clear();

        String storeString = PSP.getInstance().getString(UnreadMgrImpl.getMsgStoreTag(), "");
        if (TextUtils.isEmpty(storeString)) return;

        try {
            unreadMsgListMap = JSON.parseObject(storeString, new TypeReference<Map<Long, Integer>>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (unreadMsgListMap == null) {
                unreadMsgListMap = new HashMap<>();
            }
        }
    }

    /**
     * @return 本地存储未读数
     */
    public int registerMsgBadge(BadgeView badge, boolean isPoint, long key) {
        if (badge == null || key < 0) return 0;

        int num = getMsgKeyNum(key);

        if (num < 1) {//空内容
            badge.setVisibility(View.GONE);
        } else {
            badge.setVisibility(View.VISIBLE);
            if (isPoint) {
                badge.setPoint();
            } else {
                badge.setText(getUnreadTotalNum(num));
            }
        }
        return num;
    }

    /**
     * @param key 角标 + 1
     */
    public void addMsgNumUnread(long key) {
        addMsgNumUnread(key, 1);
    }

    /**
     * @param key 角标 + addNum
     */
    public void addMsgNumUnread(long key, int addNum) {
        if (key < 0) return;
        int num = getMsgKeyNum(key);
        num += addNum;
        unreadMsgListMap.put(key, num);//存储子角标
        castMsgListUnread(key, true);
    }

    public int resetMsgUnreadByKey(long key) {
        int removeValue = 0;
        if (key < 0) return removeValue;

        try {
            removeValue = unreadMsgListMap.remove(key);
        } catch (Exception e) {
        }
        castMsgListUnread(key, false);
        return removeValue;
    }

    private Map<String, Object> msgListMap;
    private void castMsgListUnread(long key, boolean isAdd) {
        if (unreadListener != null) {
            unreadListener.onMsgUnreadChange(key, JSON.toJSONString(unreadMsgListMap));
        }

        if (msgListMap == null) {
            msgListMap = new HashMap<>();
        }
        msgListMap.clear();
        msgListMap.put(Msg_Name_Key, key);
        msgListMap.put(Msg_Status_Key, isAdd);
        MsgMgr.getInstance().sendMsg(MsgType.MT_Msg_Unread, msgListMap);
    }

    public int getMsgKeyNum(long key) {
        Integer integer = unreadMsgListMap.get(key);
        return integer == null ? 0 : integer;
    }


    // =================================== 应用未读：外部调用 ===================================

    /**
     * 设置未读角标变动监听
     */
    public void setUnreadListener(UnreadListener unreadListener) {
        this.unreadListener = unreadListener;
    }

    /**
     * 通过角标的key获取存储的角标对象的显示数目。只针对数值类角标
     *
     * @param key 角标key值
     * @return 存储的单个角标对象
     */
    public int getUnreadNumByKey(String key) {
        return getKeyNum(key);
    }

    /**
     * 注册角标view，写在页面resume的位置，保证每次用户可见都会更新角标
     *
     * @param badge   注册角标的view
     * @param isPoint 是否只显示角标点
     * @param key     注册角标的key
     */
    public void registerBadge(BadgeView badge, boolean isPoint, String key) {
        if (badge == null || TextUtils.isEmpty(key)) return;

        int num = getKeyNum(key);

        PLogger.d("registerView：key：" + key + "，isPoint：" + isPoint + ", " + num);
        if (key.equals(UnreadMgrImpl.MY_FRIEND) || key.equals(UnreadMgrImpl.MAIL)) {
            unreadMap.put(key, num);
        }
        if (num < 1) {//空内容
            badge.setVisibility(View.GONE);
        } else {
            badge.setVisibility(View.VISIBLE);
            if (isPoint) {
                badge.setPoint();
            } else {
                badge.setText(getUnreadTotalNum(num));
            }
        }
    }

    /**
     * 添加数字角标，每次单独+1
     *
     * @param key 角标的key
     */
    public void addNumUnread(String key) {
        addNumUnread(key, 1);
    }

    public void addNumUnread(String key, int addNum) {
        if (TextUtils.isEmpty(key)) return;

        int num = getKeyNum(key);
        num += addNum;
        unreadMap.put(key, num);//存储子角标

        addParent(key, addNum);
        PLogger.d("addNumUnread: --------->key：" + key + "，unreadMap：" + unreadMap.toString());

        castUnreadMsg(key, true);
    }

    /**
     * 从存储中移除指定key值的一个未读值
     */
    public void reduceUnreadByKey(String key) {
        reduceUnreadByKey(key, 1);
    }

    /**
     * 从存储中移除指定key值的 reduceNum 个未读值
     *
     * @param key 未读key
     */
    public void reduceUnreadByKey(String key, int reduceNum) {
        int num = getKeyNum(key);
        if (TextUtils.isEmpty(key) || num <= 0) return;

        reduceParent(key, reduceNum);//父级角标递减

        //子级角标递减
        if (num <= 1 || reduceNum >= num) {
            unreadMap.remove(key);
        } else {
            num -= reduceNum;
            unreadMap.put(key, num);
        }
        PLogger.d("reduceUnreadByKey: --------->key：" + key + "，unreadMap：" + unreadMap.toString());

        castUnreadMsg(key, false);
    }

    /**
     * 从存储中移除指定key值的所有未读信息。
     *
     * @param key 未读key
     */
    public void resetUnreadByKey(String key) {
        if (TextUtils.isEmpty(key)) return;

        clearChildInParent(key);//先移除父级再移除子级，因为父级的计算要依赖于子级
        unreadMap.remove(key);
        PLogger.d("resetUnreadByKey: --------->key：" + key + "，unreadMap：" + unreadMap.toString());

        castUnreadMsg(key, false);
    }

    /**
     * 清除所有存储的未读角标
     */
    public void resetAllUnread() {
        unreadMap.clear();
        PLogger.d("resetAllUnread: --------->" + unreadMap.toString());
        castUnreadMsg(null, false);
    }

    // ================================ 角标数字 ========================

    /**
     * 未读设置
     */
    public void onMsgNum(BadgeView badgeView, String key) {
        int num = getUnreadNumByKey(key);
        badgeView.setText(getUnreadTotalNum(num));
        badgeView.setVisibility(num > 0 ? View.VISIBLE : View.GONE);
    }

    /**
     * 转换消息角标个数
     */
    public String getUnreadNum(int unreadNum) {
        return unreadNum <= 9 ? String.valueOf(unreadNum) : "9+";
    }

    public String getUnreadTotalNum(int unreadNum) {
        return unreadNum <= 99 ? String.valueOf(unreadNum) : "99+";
    }

    private int getKeyNum(String key) {
        Integer integer = unreadMap.get(key);
        return integer == null ? 0 : integer;
    }


    // =================================== 内部调用 ===================================

    private UnreadListener unreadListener;

    /**
     * 抛出角标变动消息的监听
     */
    public interface UnreadListener {
        /**
         * 角标变动
         *
         * @param key         角标类型的key值
         * @param isAdd       是否为角标添加消息：true[添加]，false[减少]
         * @param storeString 需要存储的角标信息json字符串，可由外部调用选择存储至数据库或SP等
         */
        void onUnreadChange(String key, boolean isAdd, String storeString);

        /**
         * 消息列表角标变动
         */
        void onMsgUnreadChange(long key, String storeString);
    }

    /**
     * 抛出角标变动消息
     *
     * @param key   角标类型的key值
     * @param isAdd 是否为角标添加消息：true[添加]，false[减少]
     */
    private Map<String, Object> msgMap;

    private void castUnreadMsg(String key, boolean isAdd) {
        if (unreadListener != null) {
            unreadListener.onUnreadChange(key, isAdd, JSON.toJSONString(unreadMap));
        }

        if (msgMap == null)
            msgMap = new HashMap<>();
        msgMap.clear();
        msgMap.put(Msg_Name_Key, key);
        msgMap.put(Msg_Status_Key, isAdd);
        MsgMgr.getInstance().sendMsg(MsgType.MT_Unread_change, msgMap);
    }

    /**
     * 根据子级角标的key递增父级角标。父级角标只关心num，不关心其他字段，isPoint默认为false，show默认为""
     *
     * @param key 子级角标的key
     */
    private void addParent(String key) {
        addParent(key, 1);
    }

    private void addParent(String key, int num) {
        if (TextUtils.isEmpty(key) || parentMap == null) return;
        String[] parent = parentMap.get(key);
        if (parent == null) return;//如果没有父级角标，就返回

        for (String s : parent) {
            int parentNum = getKeyNum(s);
            parentNum += num;
            unreadMap.put(s, parentNum);//存储父角标
        }
    }

    /**
     * 根据子级角标的key递减父级角标.。父级角标只关心num，不关心其他字段，isPoint默认为false，show默认为""
     *
     * @param reduceNum 减少的数字
     */
    private void reduceParent(String key, int reduceNum) {
        String[] parent = parentMap.get(key);
        if (parent == null) return;//如果没有该类型角标或者没有该类型的父级角标，就返回

        for (String s : parent) {
            int parentNum = getKeyNum(s);
            //不管是数字还是文字角标，如果父级角标的个数<=1，就从集合中移除该父级角标。否则角标-1
            if (parentNum <= 1 || reduceNum >= parentNum) {
                unreadMap.remove(s);
            } else {
                parentNum -= reduceNum;
                unreadMap.put(s, parentNum);
            }
        }
    }

    /**
     * 在父级角标中清除指定的子级角标
     *
     * @param key 子级角标的key
     */
    private void clearChildInParent(String key) {
        int num = getKeyNum(key);
        String[] parent = parentMap.get(key);
        if (parent == null) return;//如果没有该类型角标或者没有该类型的父级角标，就返回

        for (String s : parent) {
            int parentNum = getKeyNum(s);
            if (num >= parentNum) {//如果子级角标的个数>=父级角标的个数，就从集合中移除该父级角标
                unreadMap.remove(s);
            } else {
                parentNum -= num;
                unreadMap.put(s, parentNum);
            }
        }
    }
}